package com.bluesky.plum.richdomain;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.bluesky.plum.richdomain.annotation.field.OUMField;
import com.bluesky.plum.richdomain.annotation.field.OUMStringField;
import com.bluesky.plum.richdomain.annotation.parameter.OUMParameter;
import com.bluesky.plum.uimodels.standard.StandardUIComponentType;

/**
 * This enum map the annotation and RichDomainXXXField
 * 
 * @author HZ00260
 * 
 */
public enum FieldType {

	Void {
		@Override
		public StandardUIComponentType getComponentType(RichDomainField arg0) {

			return StandardUIComponentType.TextBox;
		}

	},
	Undefined {
		@Override
		public StandardUIComponentType getComponentType(RichDomainField arg0) {

			return null;
		}
	},
	Integer {
		@Override
		public void readParameterAnnotations(
				Map<Class, Annotation> paramAnnotationMap, RichDomainField rdf) {

			super.readParameterAnnotations(paramAnnotationMap, rdf);

			if (paramAnnotationMap.containsKey(OUMParameter.class)) {
				OUMParameter oumParameter = (OUMParameter) paramAnnotationMap
						.get(OUMParameter.class);

				rdf.value = oumParameter.defaultIntegerValue();
			}
		}

		@Override
		public StandardUIComponentType getComponentType(RichDomainField arg0) {

			return StandardUIComponentType.SpinBox;
		}
	},
	Float() {
		@Override
		public void readParameterAnnotations(
				Map<Class, Annotation> paramAnnotationMap, RichDomainField rdf) {

			super.readParameterAnnotations(paramAnnotationMap, rdf);

			if (paramAnnotationMap.containsKey(OUMParameter.class)) {
				OUMParameter oumParameter = (OUMParameter) paramAnnotationMap
						.get(OUMParameter.class);

				rdf.value = oumParameter.defaultBooleanValue();
			}
		}

		@Override
		public StandardUIComponentType getComponentType(RichDomainField arg0) {

			return StandardUIComponentType.TextBox;
		}
	},
	BigDecimal() {
		@Override
		public void readParameterAnnotations(
				Map<Class, Annotation> paramAnnotationMap, RichDomainField rdf) {

			super.readParameterAnnotations(paramAnnotationMap, rdf);

			if (paramAnnotationMap.containsKey(OUMParameter.class)) {
				OUMParameter oumParameter = (OUMParameter) paramAnnotationMap
						.get(OUMParameter.class);

				rdf.value = oumParameter.defaultBooleanValue();
			}
		}

		@Override
		public StandardUIComponentType getComponentType(RichDomainField arg0) {

			return StandardUIComponentType.TextBox;
		}
	},
	Boolean() {
		@Override
		public void readParameterAnnotations(
				Map<Class, Annotation> paramAnnotationMap, RichDomainField rdf) {

			super.readParameterAnnotations(paramAnnotationMap, rdf);

			if (paramAnnotationMap.containsKey(OUMParameter.class)) {
				OUMParameter oumParameter = (OUMParameter) paramAnnotationMap
						.get(OUMParameter.class);

				rdf.value = oumParameter.defaultBooleanValue();
			}
		}

		@Override
		public StandardUIComponentType getComponentType(RichDomainField arg0) {

			return StandardUIComponentType.Checkbox;
		}
	},
	String() {
		@Override
		public void readFieldAnnotation(Field f, RichDomainField rdf) {
			super.readFieldAnnotation(f, rdf);

			RichDomainStringField rdsf = (RichDomainStringField) rdf;
			if (f.isAnnotationPresent(OUMStringField.class)) {
				rdsf.maxLength = f.getAnnotation(OUMStringField.class)
						.maxLength();

			}
		}

		@Override
		public void readParameterAnnotations(
				Map<Class, Annotation> paramAnnotationMap, RichDomainField rdf) {

			super.readParameterAnnotations(paramAnnotationMap, rdf);

			if (paramAnnotationMap.containsKey(OUMParameter.class)) {
				OUMParameter oumParameter = (OUMParameter) paramAnnotationMap
						.get(OUMParameter.class);

				rdf.value = oumParameter.defaultStringValue();
			}
		}

		@Override
		public RichDomainField createRichDomainField() {

			return new RichDomainStringField();
		}

		@Override
		public StandardUIComponentType getComponentType(RichDomainField arg0) {

			return StandardUIComponentType.TextBox;
		}
	},
	DateTime() {

		@Override
		public void readParameterAnnotations(
				Map<Class, Annotation> paramAnnotationMap, RichDomainField rdf) {

			super.readParameterAnnotations(paramAnnotationMap, rdf);

			if (paramAnnotationMap.containsKey(OUMParameter.class)) {
				OUMParameter oumParameter = (OUMParameter) paramAnnotationMap
						.get(OUMParameter.class);

				if (oumParameter.defaultDateValue().isEmpty())
					rdf.value = new Date();
			}
		}

		@Override
		public StandardUIComponentType getComponentType(RichDomainField arg0) {

			return StandardUIComponentType.DateTimePicker;
		}
	},
	Collection() {
		@Override
		public void readFieldAnnotation(Field field, RichDomainField rdf) {

			super.readFieldAnnotation(field, rdf);

			RichDomainObjectField rdof = (RichDomainObjectField) rdf;

			if (field.isAnnotationPresent(OUMField.class)) {
				OUMField oumField = field.getAnnotation(OUMField.class);
				rdof.newInstance = oumField.own();
			}
		}

		@Override
		public void readParameterAnnotations(
				Map<Class, Annotation> paramAnnotationMap, RichDomainField rdf) {
			RichDomainObjectField rdof = (RichDomainObjectField) rdf;
			super.readParameterAnnotations(paramAnnotationMap, rdf);

			if (paramAnnotationMap.containsKey(OUMParameter.class)) {
				OUMParameter oumParameter = (OUMParameter) paramAnnotationMap
						.get(OUMParameter.class);

				rdof.newInstance = oumParameter.newInstance();
			}
		}

		@Override
		public StandardUIComponentType getComponentType(RichDomainField rdf) {

			RichDomainObjectField rdof = (RichDomainObjectField) rdf;
			if (rdof.newInstance)
				return StandardUIComponentType.ObjectCollectionEditor;
			else
				return StandardUIComponentType.ObjectBrowser;
		}

		@Override
		public RichDomainField createRichDomainField() {

			return new RichDomainObjectField();
		}
	},
	ObjectReference() {
		@Override
		public void readFieldAnnotation(Field field, RichDomainField rdf) {

			super.readFieldAnnotation(field, rdf);

			RichDomainObjectField rdof = (RichDomainObjectField) rdf;

			if (field.isAnnotationPresent(OUMField.class)) {
				OUMField oumField = field.getAnnotation(OUMField.class);
				rdof.newInstance = oumField.own();
			}
		}

		@Override
		public void readParameterAnnotations(
				Map<Class, Annotation> paramAnnotationMap, RichDomainField rdf) {
			RichDomainObjectField rdof = (RichDomainObjectField) rdf;
			super.readParameterAnnotations(paramAnnotationMap, rdf);

			if (paramAnnotationMap.containsKey(OUMParameter.class)) {
				OUMParameter oumParameter = (OUMParameter) paramAnnotationMap
						.get(OUMParameter.class);

				rdof.newInstance = oumParameter.newInstance();
			}
		}

		@Override
		public StandardUIComponentType getComponentType(RichDomainField rdf) {
			RichDomainObjectField rdof = (RichDomainObjectField) rdf;
			if (rdof.newInstance)
				return StandardUIComponentType.ObjectEditor;
			else
				return StandardUIComponentType.ObjectBrowser;
		}

		@Override
		public RichDomainField createRichDomainField() {

			return new RichDomainObjectField();
		}
	};

	private FieldType() {

	}

	public void readFieldAnnotation(Field f, RichDomainField rdf) {

		if (f.isAnnotationPresent(OUMField.class)) {
			OUMField oumField = f.getAnnotation(OUMField.class);

			if (!oumField.caption().equals(""))
				rdf.caption = oumField.caption();

			rdf.description = oumField.desc();

			rdf.position = oumField.position();
			rdf.enabled = oumField.enabled();
		}
	}

	public void readParameterAnnotations(
			Map<Class, Annotation> paramAnnotationMap, RichDomainField rdf) {

		if (paramAnnotationMap.containsKey(OUMParameter.class)) {
			OUMParameter oumParameter = (OUMParameter) paramAnnotationMap
					.get(OUMParameter.class);

			// has refClass, read Field info first, then override with local
			// info
			if (oumParameter.refClass() != Object.class) {
				String fieldName = oumParameter.name();
				if (!oumParameter.refField().equals(""))
					fieldName = oumParameter.refField();

				try {
					Field field = oumParameter.refClass().getDeclaredField(
							fieldName);

					readFieldAnnotation(field, rdf);

				} catch (Exception e) {
					throw new RuntimeException(e);
				}

			}

			rdf.name = oumParameter.name();

			if (!oumParameter.caption().isEmpty())
				rdf.caption = oumParameter.caption();

			if (!oumParameter.desc().isEmpty())
				rdf.description = oumParameter.desc();

		} else {
			rdf.name = "P";
			rdf.caption = rdf.name;
			rdf.description = "";

		}

		if (rdf.caption == null || rdf.caption.isEmpty())
			rdf.caption = rdf.name;
	}

	public RichDomainField createRichDomainField() {
		return new RichDomainField();
	}

	public static FieldType getType(Class clazz) {
		FieldType fieldType = null;

		String className = clazz.getSimpleName();

		if (clazz.toString().equals("void"))
			fieldType = FieldType.Void;
		else if (clazz == Integer.class || clazz == Long.class) {
			fieldType = FieldType.Integer;

			System.out.println("class loader:" + clazz.getClassLoader());
		} else if (clazz == Float.class || clazz == Double.class)
			fieldType = FieldType.Float;
		else if (clazz == BigDecimal.class)
			fieldType = FieldType.BigDecimal;
		else if (clazz == String.class)
			fieldType = FieldType.String;
		else if (clazz == Boolean.class)
			fieldType = FieldType.Boolean;
		else if (clazz == Date.class)
			fieldType = FieldType.DateTime;
		else if (clazz == List.class || clazz == Set.class || clazz.isArray())
			fieldType = FieldType.Collection;
		else {
			if (clazz.getPackage() != null
					&& clazz.getPackage().getName() != null
					&& clazz.getPackage().getName().startsWith("com."))
				fieldType = FieldType.ObjectReference;
			else
				fieldType = FieldType.Undefined;

			System.out.println("FieldType.getType(" + clazz + ") " + fieldType);
		}

		if (className.equals("Integer") && fieldType != FieldType.Integer) {
			System.out.println("Integer from different class loader:"
					+ clazz.getClassLoader());
		}

		return fieldType;

	}

	abstract public StandardUIComponentType getComponentType(RichDomainField rdf);

	public static void main(String[] args) {

	}
}
